require"std"
require"vector"

function randomOrientation(object)
  local axis, angle=vector.unit{1-math.random(), 1-math.random(), 1-math.random()},math.random()*2*math.pi+0.01
  for vertex in pairs(object.allVertices) do
    vertex[1],vertex[2],vertex[3]=unpack(vector.rotate(vertex,axis,angle))
  end
  return object
end


function extractAllVertices(object)
  local all={}
  for i, surface in ipairs(object.surfaces) do
    all[surface.center]=true
    all[surface.normal]=true
    local polygon=surface.polygon
    while polygon do
      for i, vertex in ipairs(polygon) do all[vertex]=true end
      polygon=polygon.next 
    end
    local texture=surface.texture
    while texture do
      all[texture.o]=true
      all[texture.x]=true
      all[texture.y]=true
      texture=texture.next
    end
  end
return all end


function polygonToSurface(polygon)
  local surface={}
  surface.center=math.avg(unpack(polygon))
  surface.normal=vector.unit(vector.cross(polygon[1]-polygon[2], polygon[3]-polygon[2]))
  if vector.dot(surface.normal,surface.center)>0 then surface.normal=-surface.normal end
  surface.polygon=polygon
return surface end

function verticesToPolygons(vertices) local result={} 

  --this inner functions checks each vertex against a triangle
  --if it is coplanar, we add it to the current face
  --if it's on either side of the triangle, we use this info to see if the triangle itself is a surface or an inner triangle
  --we also make sure that each face is reported only once
  local function face(i,j,k) local result={}
    local normal=vector.cross(vertices[j]-vertices[i], vertices[k]-vertices[i])
    local reference=0
    for p=1,#vertices do
      local projection=vector.dot(normal, vertices[p]-vertices[i])
      if math.abs(projection)<0.00001 then --coplanar case
        if p<k and p~=i and p~=j then return nil-- already accounted for when i or j was equal to p
        else table.insert(result,p) end
      elseif reference*projection<0 then return nil--two points on the opposite side of the triangle -> not part of the convex hull
      else reference=projection end
    end
  return table.map(result, func.pswap(__index), vertices) end --convert indices to vectors

  --this inner function takes a face and makes it a simple, convex polygon by sorting the vertices 
  local function simplify(face) if face then 
    local center=math.avg(unpack(face))
    local x=vector.unit(face[1]-center)
    local z=vector.cross(x,vector.unit(face[2]-face[1]))
    local y=vector.cross(z,x)
    local angles={}
    for _,v in ipairs(face) do 
      angles[v]=math.atan2(vector.dot(v-center, y), vector.dot(v-center, x)) 
    end
    table.sort(face,function(a,b) return angles[a]<angles[b] end)
  end return face end

  for i=1, #vertices do for j=i+1,#vertices do for k=j+1,#vertices do
    table.insert(result,simplify(face(i,j,k)))
  end end end
return result end

--d4
function v4() return {Vector{-1,-1,-1},Vector{-1,1,1},Vector{1,-1,1},Vector{1,1,-1}} end


--[[ the functions v4, v6, v8, v10, v12 and v20 return a set of vertices that correspond to a particular die shape

function v6() local result={}
  for x=-1,1,2 do for y=-1,1,2 do for z=-1,1,2 do table.insert(result,vector{x,y,z}) end end end
return result end

function v8() return {vector{1,0,0},vector{0,1,0},vector{0,0,1},vector{-1,0,0},vector{0,-1,0},vector{0,0,-1}} end

function v20() local result=v8() --a d20 is a d8 with all its vertices split into two by the golden ratio
  for i=1,#result do
    local v=result[i]
    local split=vector{v[1],v[2],v[3]}
    for c=1, 3 do if v[c]~=0 then --fint the nonzero coordinate and modify the next
      c=math.mod1(c+1,3)
      v[c]= 1/1.6180339887498948482
      split[c]=-v[c]
      table.insert(result,split)
    break end end
  end
return result end

function v12() return table.map(hull(v20()),func.combine(math.avg, unpack)) end --a d12's vertices are the centers of a d20 faces

function v10() --a d10 is a d12 with two opposite faces joined into two points
  local vx=v12()
  local hull=hull(vx)
  local function extrude(i)
    table.insert(vx,center(hull[i])*2.23605) --experimental number
    for j=1,#hull[i] do table.remove(vx,table.find(vx,hull[i][j])) end
  end
  extrude(1) --these are good numbers for this particular v12/hull algorithm pair. 
  extrude(11) -- in general it would be better to find the opposite faces instead of hardcoding them
return vx end
--]]

